package com.aizuda.encrypt.interceptor;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.aizuda.encrypt.entity.ColumnRule;
import com.aizuda.encrypt.entity.FieldInfo;
import com.aizuda.encrypt.enums.EncryptType;
import com.aizuda.encrypt.util.DecryptUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Statement;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;

/**
 * Mybatis拦截器 - 解密数据查询拦截
 *
 * @author nn200433
 * @date 2024-03-25 05:06:43
 */
@Slf4j
@Intercepts({@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})})
public class DecryptQueryInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 取出查询的结果
        Object resultObject = invocation.proceed();
        if (ObjUtil.isNull(resultObject)) {
            return null;
        }
        final Class<?> clz = resultObject.getClass();
        if (resultObject instanceof Collection || clz.isArray()) {
            // 基于selectList
            List<?> resultList = Convert.toList(resultObject);
            if (CollUtil.isNotEmpty(resultList)) {
                // 取出非空的第一个对象
                final Optional<?> first = resultList.parallelStream().filter(ObjUtil::isNotNull).findFirst();
                if (first.isPresent()) {
                    final Object   o    = first.get();
                    final Class<?> oClz = o.getClass();
                    if (ObjUtil.isNotNull(o) && DecryptUtil.isNeedDecrypt(oClz)) {
                        resultList.forEach(r -> decryptHandler(r, oClz));
                    }
                }
            }
        } else {
            // 基于selectOne
            if (DecryptUtil.isNeedDecrypt(clz)) {
                decryptHandler(resultObject, clz);
            }
        }
        return resultObject;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }

    /**
     * 解密处理程序
     *
     * @param result 后果
     * @param clz    clz
     * @author nn200433
     */
    private void decryptHandler(Object result, Class<?> clz) {
        final Map<Field, FieldInfo> fieldMap = DecryptUtil.getDecryptField(clz);
        for (final Map.Entry<Field, FieldInfo> entry : fieldMap.entrySet()) {
            final Field       field       = entry.getKey();
            final FieldInfo   fInfo       = entry.getValue();
            final String      tableName   = fInfo.getTableName();
            final String      column      = fInfo.getColumn();
            final String      encryptKey  = fInfo.getEncryptKey();
            final EncryptType encryptType = fInfo.getEncryptType();
            final ColumnRule  rule        = fInfo.getRule();
            final Boolean     encryptor   = rule.getEncryptor();
            Object            value       = null;
            try {
                final Method invokeGet = fInfo.getInvokeGet();
                final Method invokeSet = fInfo.getInvokeSet();
                value = invokeGet.invoke(result);
                if (null == value) {
                    // null 对象不解密
                    continue;
                }
                final Class<?> vClz = value.getClass();
                if (!vClz.equals(String.class)) {
                    log.warn("---> 解密 {} 时，发现该值不是 String 类型，不予解密！", field.getName());
                    continue;
                }
                if (!encryptor) {
                    // 未开启加密状态，则跳过
                    continue;
                }
                final String valueStr = Convert.toStr(value);
                if (StrUtil.isBlank(valueStr)) {
                    // 空字符串不解密
                    continue;
                }
                final String decrypt = DecryptUtil.decrypt(encryptType, encryptKey, valueStr);
                invokeSet.invoke(result, decrypt);
                log.debug("---> [Mybatis SQL 解密拦截器] 解密方式 = {} | 译文 = {} | 原文 = {}", encryptType, decrypt, valueStr);
            } catch (Exception e) {
                log.error("---> [Mybatis 解密操作] 加密数据 {}.{} = {} 解密异常...", tableName, column, value);
            }
        }
    }

}
